using System;
using System.Reflection;
using NHibernate;
using NHibernate.Properties;

namespace Technoera.Common.Nh.CustomPropertyAccessors
{
    public class EnabledUnknowPropertyAccessor : IPropertyAccessor
    {
        #region IPropertyAccessor Members

        public ISetter GetSetter(Type classType, string propertyName)
        {
            NHibernate.Properties.BasicPropertyAccessor.BasicSetter result = GetSetterOrNull(classType, propertyName);
            if (result == null) throw new PropertyNotFoundException(classType, propertyName, "setter");
            return result;
        }

        public IGetter GetGetter(Type classType, string propertyName)
        {
            EnabledUnknowGetter result = GetGetterOrNull(classType, propertyName);
            if (result == null) throw new PropertyNotFoundException(classType, propertyName, "getter");
            return result;
        }

        public bool CanAccessThroughReflectionOptimizer
        {
            get { return true; }
        }

        #endregion

        internal static NHibernate.Properties.BasicPropertyAccessor.BasicSetter GetSetterOrNull(System.Type type, string propertyName)
        {
            if (type == typeof(object) || type == null)
            {
                // the full inheritance chain has been walked and we could
                // not find the Property get
                return null;
            }

            PropertyInfo property = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);

            if (property != null && property.CanWrite)
            {
                return new NHibernate.Properties.BasicPropertyAccessor.BasicSetter(type, property, propertyName);
            }
            else
            {
                // recursively call this method for the base Type
                NHibernate.Properties.BasicPropertyAccessor.BasicSetter setter = GetSetterOrNull(type.BaseType, propertyName);

                // didn't find anything in the base class - check to see if there is 
                // an explicit interface implementation.
                if (setter == null)
                {
                    System.Type[] interfaces = type.GetInterfaces();
                    for (int i = 0; setter == null && i < interfaces.Length; i++)
                    {
                        setter = GetSetterOrNull(interfaces[i], propertyName);
                    }
                }
                return setter;
            }
        }

        internal static EnabledUnknowGetter GetGetterOrNull(System.Type type, string propertyName)
        {
            if (type == typeof(object) || type == null) return null;

            //PropertyInfo property = type.GetProperty(propertyName);
            PropertyInfo property = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);

            if (property != null)
            {
                return new EnabledUnknowGetter(type, property, propertyName);
            }
            else
            {
                EnabledUnknowGetter getter = GetGetterOrNull(type.BaseType, propertyName);
                if (getter == null)
                {
                    System.Type[] interfaces = type.GetInterfaces();
                    for (int i = 0; getter == null && i < interfaces.Length; i++)
                    {
                        getter = GetGetterOrNull(interfaces[i], propertyName);
                    }
                }
                return getter;
            }
        }
        
    }
}
